// 匹配标签名 my-button
// 第一个\：用来转译第二个\，所以第一组\\相当于\；
// 第三个\：用来转译第四个\，所以第二组\\也相当于\；
// 字符串\\-转译后，成为正则中的\-，正则中表示-；
// 字符串\\.转译后，成为正则中的\.，正则中表示.；
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`;
// 命名空间标签 aa:aa-xxx
// ?:: 表示匹配，但不捕获；
// ${ncname}: 标签名，参考ncname正则；
// \\:: 正则中的\:，表示后面有一个冒号；
// ?: 最后的问号，表示前面的内容可有可无；比如：aa:可以没有
// ${ncname} 标签名，参考ncname正则；
// 用于匹配aa:aa-xxx，这种标签叫做“命名空间标签”，很少会使用到；
const qnameCapture = `((?:${ncname}\\:)?${ncname})`;
// 开始标签-捕获标签名
// ^<：表示以<开头；
const startTagOpen = new RegExp(`^<${qnameCapture}`);
// 结束标签-匹配标签结尾的 </div>
// ^<\\/：<符号开头，后面跟一个/，即以</开头；
// ${qnameCapture}：参考命名空间标签；
// [^>]*：中间可以是任意数量不为>的字符；
// >：最后一位必须是>；
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`);
// 匹配属性这个相对比较复杂
// ^\s*： 开头为 n 个空格（0个或n个空格）
// [^\s"'<>\/=]+: 匹配属性名； 不是空格、引号、尖脚号、等号的n个字符；
// ?:\s*(=)\s*： 空格和空格之间可以夹着一个等号=；
// ?:“([“]*)”+|'([‘]*)’+|([^\s”'=<>`]+) ：匹配属性值
// "([^"]*)": 可能是双引号；
// '([^']*)': 可能是单引号；
// [^\s"'=<>`]+: 不是空格、引号、尖脚号、等号的 n 个字符；
// 属性的三种可能写法
// 情况 1：双引号的情况，如：aaa="xxx";
// 情况 2：单引号的情况，如：aaa='xxx';
// 情况 3：没引号的情况，如：aaa=xxx;
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/;
// 匹配标签结束的 >
// ^\s*: 空格 n 个
// (\/?)>: 尖角号有以下两种情况
// -/>: 自闭合
// ->: 没有/的闭合
const startTagClose = /^\s*(\/?)>/;

function createASTElement(tag, attrs, parent) {
  return {
    tag, // 标签名
    type: 1, // 元素
    children: [], // 儿子
    parent, // 父亲
    attrs // 属性
  };
}

// 将模板编译成render函数
export function compileToFunction(template) {
  const ast = parser(template);
  const code = generate(ast);
}

// 将模板解析成AST
function parser(html) {
  console.log(html, 'html');

  function advance(len) {
    html = html.substring(len);
  }

  function parseStartTag() {
    const start = html.match(startTagOpen);
    console.log(start, 'start');
    // 如果匹配到了内容，才去创建ast对象
    if (start) {
      const match = {
        tagName: start[1],
        attrs: []
      };

      advance(start[0].length);

      // debugger;askefjalsdjflasjdf
      // 紧接着要去处理属性

      let end; // 保存 匹配开始标签结束的结果
      let attr; // 保存 匹配的属性结果

      // 没有结束 并且 还有属性
      while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) {
        // while(!(end = html.match(startTagClose)) && attr = html.match(attribute))) {
        // 就要将属性添加到上面的match.attrs中
        match.attrs.push({ name: attr[1], value: attr[3] || attr[4] || attr[5] });
        advance(attr[0].length);
      }

      if (end) {
        advance(end[0].length);
      }

      return match;
    }
  }

  const stack = [];
  let root = null;

  // 开始标签
  function start(tagName, attrs) {
    console.log('start', tagName, attrs);

    // 父元素（栈顶的标签）
    const parent = stack[stack.length - 1];

    // 子元素
    const element = createASTElement(tagName, attrs, parent);

    // 第一次创建出来的就是根节点
    if (root === null) root = element;

    // 父子关系确认
    if (parent) {
      parent.children.push(element);
    }

    stack.push(element);
  }

  // 结束标签
  function end(tagName) {
    console.log('end', tagName);
    stack.pop();
  }

  // 文本标签
  function text(chars) {
    console.log('text', chars);

    const parent = stack[stack.length - 1];

    chars = chars.trim();

    if (chars) {
      parent.children.push({
        type: 2, // 1: 元素, 2: 文本
        text: chars
      });
    }
  }

  while (html) {
    const index = html.indexOf('<');

    console.log(html, 'html');
    // 第一个字符是<，说明是标签（开始或结束）
    if (index === 0) {
      // 处理开始标签
      const startTagMatch = parseStartTag();
      if (startTagMatch) {
        start(startTagMatch.tagName, startTagMatch.attrs);
        continue;
      }

      // 处理结束标签
      const endTagMath = html.match(endTag);
      if (endTagMath) {
        end(endTagMath[1]);
        advance(endTagMath[0].length);
        continue;
      }
    }
    // 第一个字符不是<，说明是文本
    else {
      const chars = html.substring(0, index);
      text(chars);
      advance(chars.length);
      continue;
    }
  }
}

// 生成render函数的代码
function generate(ast) {}
